﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using Formsy.Config;
using Formsy.Data.Services;
using System.Data.SqlTypes;
using Anthony.Library;
using Formsy.Data.Linq;
using System.Web.UI;
using System.Reflection;
using System.Xml.Linq;
using Formsy.Data.Interfaces;
using System.Web;
using Formsy.Core.Resources;

namespace Formsy.Core
{
    public class Admin : WebControl
    {
        #region Public Properties

        // the form name as it was in the form definition
        public string FormName { get; set; }

        // comma separated list of columns to not display
        public string IgnoreList { get; set; }

        // this overide is to allow easier targetting
        public override string ClientID
        {
            get
            {
                return "FormsyAdmin";
            }
        }

        public int EntriesPerPage { get; set; }

        public bool UseDefaultStyle { get; set; }

        #endregion


        #region Protected Properties

        protected ControlBuilder m_ControlBuilder;
        protected List<IFormsyEntry> m_Entries;
        protected int m_CurrentPage;

        // used by client side script
        protected HiddenField m_HfCurrentPage;

        protected Dictionary<string, HtmlGenericControl> m_Columns;

        // allows for adding an alternative class to rows
        protected bool m_AlternateRow = false;

        #endregion
       

        #region OnLoad, OnPreRender

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            if (Page.Request.Form["Action"] != null)
                HandleAjax();
            else
            { 
                // only create controls when not an ajax call
                m_ControlBuilder = new ControlBuilder();

                // create dictionary for easy getting of column when adding values
                m_Columns = new Dictionary<string, HtmlGenericControl>();

                // create the table
                EnsureChildControls();
            }
        }

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            // add jquery
            m_ControlBuilder.AddResource(EmbededResource.AdminJQueryScript, ResourceType.Script);

            // add custom scripts
            m_ControlBuilder.AddResource(EmbededResource.AdminScript, ResourceType.Script);

            // add default style
            if (UseDefaultStyle)
            {
                m_ControlBuilder.AddResource(EmbededResource.AdminDefaultStyle, ResourceType.Style);
            }
        }

        #endregion



        #region HandleAjax

        /// <summary>
        /// Gets values passed by ajax request, and updates database.
        /// </summary>
        protected void HandleAjax()
        {
            string action = Page.Request.Form["Action"];
            int entryId = int.Parse(Page.Request.Form["EntryId"]);

            if (action == "Delete")
            {
                HttpDataService.Instance.FormsyService.DeleteEntryAndData(entryId);
            }
            else if (action == "Update")
            {
                // get values
                //string formName = Page.Request.Form["FormName"];
                string fieldName = Page.Request.Form["FieldName"];
                string value = Page.Request.Form["Value"];
                Dictionary<string, string> dictionary = new Dictionary<string, string>();
                dictionary.Add(fieldName, value);

                // update
                HttpDataService.Instance.FormsyService.UpdateEntryData(entryId, dictionary);
            }

        }

        #endregion



        #region CreateChildControls

        /// <summary>
        /// Creates a containing mainDiv, adds the form title, the csv download, and calls CreateTable to create the table entries.
        /// </summary>
        protected override void CreateChildControls()
        {
            base.CreateChildControls();
            // create containing div
            HtmlGenericControl mainDiv = m_ControlBuilder.CreateHtmlControl(ControlType.Div, ID, string.Empty);

            // add form title
            m_ControlBuilder.AddEnclosedLiteral(mainDiv, ControlType.H3, "Form Name", FormName, string.Empty);

            // add csv download
            var csv = new CSVDownload();
            csv.FormName = FormName;
            csv.UseDefaultStyle = true;
            csv.RequiredValidatorsEnabled = false;
            csv.IgnoreList = IgnoreList;
            mainDiv.Controls.Add(csv);

            // add form name as hidden field. - we don't really need to do this
            AddHiddenField(mainDiv, "FormName", FormName);
            CreateTable(mainDiv);
        }

        #endregion



        #region CreateTable

        /// <summary>
        /// Creates the table of entries with pagination.
        /// </summary>
        protected void CreateTable(HtmlGenericControl mainDiv)
        {
            // add an "add" button
            // AddAddButton(mainDiv);

            // get all entries
            HttpDataService.Instance.FormsyService.EnsureFormExists(FormName);
            m_Entries = HttpDataService.Instance.FormsyService.GetEntries(FormName, SqlDateTime.MinValue.Value, SqlDateTime.MaxValue.Value);

            // add controls for pagination
            AddPagination(mainDiv);

            // get headings
            List<string> headings = HttpDataService.Instance.FormsyService.GetHeadings(FormName, IgnoreList.Split(",".ToArray()).ToList());

            // add headings for each column
            AddHeadings(mainDiv, headings);

            // get subset of entries for page
            var entryRange = GetEntriesForPage();

            // display the entries
            foreach (var entry in entryRange)
            {
                // get entry's data xml and serialize to list
                List<FormsyEntryData> datas = (List<FormsyEntryData>)XmlSerializing.FromXml(entry.Xml, typeof(List<FormsyEntryData>));
                foreach (string heading in headings)
                {
                    // create div cell
                    HtmlGenericControl cell = m_ControlBuilder.CreateHtmlControl(ControlType.Div, "cell", string.Empty);

                    CreateCell(cell, datas, heading, entry);
                    if (m_AlternateRow)
                        m_ControlBuilder.AddClass(cell, "alternate");

                    colNumber++;
                }
                // toggle flag
                m_AlternateRow = m_AlternateRow == true ? false : true;
                colNumber = 0;
                rowNumber++;
            }

            // add containing div to page
            Controls.Add(mainDiv);
        }

        int colNumber = 0;
        int rowNumber = 0;

        /// <summary>
        /// 
        /// </summary>
        /// <returns>A subset of entries for the current page.</returns>
        protected List<IFormsyEntry> GetEntriesForPage()
        {
            int index = EntriesPerPage * (m_CurrentPage - 1);
            int subsetCount = EntriesPerPage;

            //if (ENTRIES_PER_PAGE > m_Entries.Count)
            //{
            //    // not enough entries for a full page so display all
            //    subsetCount = m_Entries.Count - 1;
            //}

            if ((index + EntriesPerPage) > m_Entries.Count)
            {
                // not enough entries for a full page so display remaining
                subsetCount = m_Entries.Count - index;
            }

            return m_Entries.GetRange(index, subsetCount);
        }


        #endregion



        #region AddPagination
        /// <summary>
        /// Adds "next" and "previous" controls for pagination.
        /// </summary>
        /// <param name="mainDiv"></param>
        protected void AddPagination(HtmlGenericControl mainDiv)
        {
            // get current page number
            int pageNo = 1;
            if (!string.IsNullOrEmpty(Page.Request.QueryString["page"]))
            {
                pageNo = int.Parse(Page.Request.QueryString["page"]);
            }
            m_CurrentPage = pageNo;


            // add current page for pagination
            m_HfCurrentPage = m_ControlBuilder.CreateHiddenField("CurrentPage", m_CurrentPage.ToString());
            mainDiv.Controls.Add(m_HfCurrentPage);

            // add page count for pagination
            //m_HfPageCount = m_ControlBuilder.CreateHiddenField("PageCount", GetPageCount().ToString());
            //mainDiv.Controls.Add(m_HfPageCount);

            //// add entries per page for pagination
            //m_HfEntriesPerPage = m_ControlBuilder.CreateHiddenField("EntriesPerPage", ENTRIES_PER_PAGE.ToString());
            //mainDiv.Controls.Add(m_HfEntriesPerPage);

            //// add entries per page for pagination
            //m_HfEntriyCount = m_ControlBuilder.CreateHiddenField("EntryCount", m_Entries.Count.ToString());
            //mainDiv.Controls.Add(m_HfEntriyCount);


            // add pagination controls container
            HtmlGenericControl div = m_ControlBuilder.CreateHtmlControl(ControlType.Div, "PageInfo", string.Empty);

            // add entry count container
            HtmlGenericControl entryCountDiv = (HtmlGenericControl)m_ControlBuilder.AddEnclosedLiteral(div, ControlType.Div, "EntryCount", "Number of entries: ", string.Empty);

            // add entry count value
            HtmlGenericControl entryCount = m_ControlBuilder.CreateLiteral("EntryCountValue", m_Entries.Count.ToString());

            entryCountDiv.Controls.Add(entryCount);


            // add "page x of y" container
            HtmlGenericControl pageCountDiv = (HtmlGenericControl)m_ControlBuilder.AddEnclosedLiteral(div, ControlType.Div, "Page Count", "Page " + m_CurrentPage + " of ", string.Empty);


            // add entry page value
            HtmlGenericControl pageCount = m_ControlBuilder.CreateLiteral("PageCountValue", GetPageCount().ToString());
            pageCountDiv.Controls.Add(pageCount);



            // add container for pagination
            HtmlGenericControl pageNav = m_ControlBuilder.CreateHtmlControl(ControlType.Div, "Pagination", string.Empty);

            // add previous link
            if (m_CurrentPage == 1)
            {
                // when on first page, only add a literal, not a link!
                m_ControlBuilder.AddEnclosedLiteral(pageNav, ControlType.Span, "Prev", "Prev", "faded");
            }
            else
            {
                // not on first page, so add the link to prev
                string link = Page.Request.Url.AbsolutePath + "?page=" + (m_CurrentPage - 1);
                HtmlAnchor previous = m_ControlBuilder.CreateLink("Prev", link, "Prev");
                pageNav.Controls.Add(previous);
            }


            // add next link
            if (m_CurrentPage == GetPageCount())
            {
                // when on first page, only add a literal, not a link!
                m_ControlBuilder.AddEnclosedLiteral(pageNav, ControlType.Div, "Next", "Next", "faded");
            }
            else
            {
                // not on last page, so add the link to next
                string link = Page.Request.Url.AbsolutePath + "?page=" + (m_CurrentPage + 1);
                HtmlAnchor next = m_ControlBuilder.CreateLink("Next", link, "Next");
                pageNav.Controls.Add(next);
            }

            div.Controls.Add(pageNav);
            mainDiv.Controls.Add(div);
        }


        #endregion



        #region CreateCell

        protected HtmlGenericControl CreateCell(HtmlGenericControl div, List<FormsyEntryData> datas, string heading, IFormsyEntry entry)
        {
            string text;
            // get text for cell 
            var data = datas.Where(x => x.Name == heading).FirstOrDefault();
            if (data == null)
            {
                // check entry for value... surely there's a better way to do this!
                if (heading == "Id")
                    text = entry.Id.ToString().Trim();
                else if (heading == "Created")
                    text = entry.Created.ToString().Trim();
                else if (heading == "Updated")
                    text = entry.Updated.ToString().Trim();
                else
                    text = "";
            }
            else
                text = data.Value.Trim();

            // make xelement to create literal
            //XElement xElem = new XElement("EntryCell");
            //xElem.Add(new XAttribute("Text", text));
            HtmlGenericControl textDiv = m_ControlBuilder.CreateHtmlControl(ControlType.Div, heading, "value");
            textDiv.InnerText = text;

            // add text to cell
            div.Controls.Add(textDiv);

            // add cell to column
            if (heading != "Delete?")
                m_Columns[heading].Controls.Add(div);

            // add edit panels with ajax calls
            if (heading == "Delete?")
            {
                CreateDeletePanel(heading, entry.Id);
            }
            else if (heading != "Id" && heading != "Created" && heading != "Updated")
            {
                // add editable class to div
                m_ControlBuilder.AddClass(div, "editable");

                // add editing panel 
                CreateEditPanel(div, heading, text, entry.Id);
            }

            // create hidden field for entry id to allow for quick editing
            AddHiddenField(div, "Id" + colNumber + "_" + rowNumber, entry.Id.ToString());

            return div;
        }

        #endregion




        #region CreateEditPanel

        /// <summary>
        /// Creates hidden div for editing content in the given cell
        /// </summary>
        protected void CreateEditPanel(HtmlGenericControl cell, string heading, string text, int entryId)
        {
            // create containing div for edit panel
            HtmlGenericControl div = m_ControlBuilder.CreateHtmlControl(ControlType.Div, "EditPanel", string.Empty);

            // create text area
            TextBox textbox = m_ControlBuilder.CreateTextArea("EditBox" + colNumber + "_" + rowNumber);
            textbox.Text = text;
            div.Controls.Add(textbox);

            // create accept image
            HtmlImage accept = m_ControlBuilder.CreateHtmlImage("accept" + colNumber + "_" + rowNumber, Page.ClientScript.GetWebResourceUrl(GetType(), EmbededResource.AdminAcceptImage), "Accept", "Accept");
            m_ControlBuilder.AddClass(accept, "accept");
            div.Controls.Add(accept);

            // create cancel image
            HtmlImage cancel = m_ControlBuilder.CreateHtmlImage("cancel" + colNumber + "_" + rowNumber, Page.ClientScript.GetWebResourceUrl(GetType(), EmbededResource.AdminCancelImage), "Cancel", "Cancel");
            m_ControlBuilder.AddClass(cancel, "cancel");
            div.Controls.Add(cancel);

            // hide the edit pannel
            div.Attributes.Add("style", "display:none");

            // add panel to column
            cell.Controls.Add(div);
        }

        #endregion




        #region CreateDeletePanel

        protected void CreateDeletePanel(string heading, int entryId)
        {
            // create containing div for delete panel
            HtmlGenericControl div = m_ControlBuilder.CreateHtmlControl(ControlType.Div, "EditPanel", "Cell");

            if (m_AlternateRow)
                m_ControlBuilder.AddClass(div, "alternate");

            // create hidden field for entry id to allow for quick editing
            //HiddenField hidden = m_ControlBuilder.CreateHiddenField("Id", entryId.ToString());
            //div.Controls.Add(hidden);
            AddHiddenField(div, "Id" + colNumber + "_" + rowNumber, entryId.ToString());


            // create delete image
            HtmlImage delete = m_ControlBuilder.CreateHtmlImage("delete" + colNumber + "_" + rowNumber, Page.ClientScript.GetWebResourceUrl(GetType(), EmbededResource.AdminDeleteImage), "Delete", "Delete");

            m_ControlBuilder.AddClass(delete, "delete");
            div.Controls.Add(delete);

            //// create confirmation
            //HtmlGenericControl deleteConfDiv = m_ControlBuilder.CreateHtmlControl(ControlType.Div, "DeleteConfirmation", string.Empty);
            //HtmlGenericControl yes = m_ControlBuilder.CreateLiteral((ControlType.Literal, "DeleteConfirmation", string.Empty);


            // add panel to column
            m_Columns[heading].Controls.Add(div);
        }

        #endregion




        #region CreateHeadings

        /// <summary>
        /// Gets headings from FormsyForm table, creates divs and literals for each heading, then puts them in the main div.
        /// </summary>
        /// <param name="mainDiv"></param>
        /// <returns></returns>
        protected HtmlGenericControl AddHeadings(HtmlGenericControl mainDiv, List<string> headings)
        {
            headings.Add("Delete?");

            // for each heading, create div column, then add a div with literal for heading
            foreach (string heading in headings)
            {
                HtmlGenericControl column = m_ControlBuilder.CreateHtmlControl(ControlType.Div, heading, "column");
                HtmlGenericControl div = m_ControlBuilder.CreateHtmlControl(ControlType.Div, heading, "heading");

                // add heading text to heading cell
                div.Controls.Add(new LiteralControl(heading));

                // add cell to new column
                column.Controls.Add(div);

                // add column to containing div
                mainDiv.Controls.Add(column);

                // store column 
                m_Columns.Add(heading, column);
            }
            return mainDiv;
        }

        #endregion



        #region AddHiddenField

        /// <summary>
        /// Adds a hidden field to the page
        /// </summary>
        /// <param name="div">The parent control</param>
        /// <param name="name">Name of the element</param>
        /// <param name="value">Initial value of the hidden field</param>
        protected void AddHiddenField(HtmlGenericControl div, string name, string value)
        {
            // create hidden field for form name 
            //HiddenField hidden = m_ControlBuilder.CreateHiddenField(name, value);
            HtmlInputHidden hidden = m_ControlBuilder.CreateHtmlHiddenField(name, value);
            div.Controls.Add(hidden);
        }

        #endregion



        /// <summary>
        /// Adds a button to the page which allows for adding entries on the admin page.
        /// </summary>
        /// <param name="container"></param>
        protected void AddAddButton(HtmlGenericControl container)
        {
            HtmlGenericControl div = m_ControlBuilder.CreateHtmlControl(ControlType.Div, "AddEntryButton", string.Empty);

            // create accept image
            HtmlImage addButton = m_ControlBuilder.CreateHtmlImage("AddEntryButton", Page.ClientScript.GetWebResourceUrl(GetType(), EmbededResource.AdminAddImage), "Add", "Add");
            m_ControlBuilder.AddClass(addButton, "AddEntry");

            div.Controls.Add(addButton);
            container.Controls.Add(div);
        }



        /// <summary>
        /// 
        /// </summary>
        /// <returns>The number of pages the entry list occupies.</returns>
        protected int GetPageCount()
        {
            if (m_Entries.Count < EntriesPerPage)
            {
                // not enough entries to fill a page
                return 1;
            }

            return (int)Math.Ceiling((double)m_Entries.Count / EntriesPerPage);
        }



    }
}
